spawns and animates sprites to create all sorts of visual effects. It’s Godot’s equivalent of 2D particle emitters in other game engines.
Their primary use is to add to the game feel and visual flair of existing actions. You can also use particles to draw many instances of a texture efficiently using the GPU.
In this guide, you will learn to:
Contents:
To create a particle system, you need to:
The node itself emits particles. It also controls their textures, the number of particles to spawn, and their lifetime. The material controls each particle’s motion and color.
You can either use a or a for the Process Material -> Material.
The lets you code a shader and control particles manually. The built-in comes with dozens of valuable properties. They allow us to make the particles accelerate, slow down, orbit, and more.
Texture.1 emits all particles at once,
0 emits particles evenly across the Lifetime.To control the particles’ emission, we use the Emitting property. Enabling it emits new particles.
Note: This property only has effect when has a or in its Process Material -> Material property.
| Emitting false | Emitting true |
|---|---|
The Amount determines how many particles to emit during one cycle of the duration set in Lifetime.
For example, with Lifetime set to 1.0, and
Amount set to 8, the emits eight particles in one second.
Each particle stays alive for one second.
Here are some examples of different Amount settings with a Lifetime of one second:
| 4 Amount | 8 Amount | 16 Amount |
|---|---|---|
In contrast, here are varying Lifetime examples, with an
Amount of 8:
0.1 Lifetime |
0.5 Lifetime |
1.0 Lifetime |
|---|---|---|
To control the interval between emitting particles, we can adjust the Explosiveness. A high Explosiveness makes particles spawn in a shorter time span.
When we want particles to emit at once, like for a water splash
effect, we use a high value like 1.0. In the following set
of examples, we adjusted the emission angle to showcase
Explosiveness better.
0.3 Explosiveness |
0.5 Explosiveness |
1.0 Explosiveness |
|---|---|---|
We use the Texture property to tell the particles’ look.
For example, to make a snow rain, we can use a single snowflake as
Texture.
Sometimes a particle effect should emit only once, as seen in splashes or explosions. In those cases, we enable the One Shot property. This property disables Emitting as soon as any particle is at the end of its Lifetime.
| One Shot false | One Shot true |
|---|---|
When we want each particles’ transform to be global, we toggle off the Local Coord property. This is necessary for many effects like trails, after images, smoke, fire, explosions, weather, and more.
| Local Coords true | Local Coords false |
|---|---|
The Process Material is a shader Godot uses to draw and animate the particles. It’s the most important property. Without it, the node won’t even emit the particles.
We have two options to control particles:
These features use the particles’ lifetime as a base for their effects. For instance, we can create a gradient like this:
This means each particle starts blue, and as time goes it becomes pink.
The same goes for other properties. As another example, take a look at this curve in the Scale > Scale Curve property:
It means that each particle starts small, grows bigger, and shrinks until it disappears by the end of its lifetime.
Let’s see some concrete ways to use particles in your 2D games.
You can use particles to draw trails, like smoke coming out of a car’s exhaust pipe or plasma behind a ship.
Trails like that emphasize movement.
For a trail like the above, we use a low Time -> Lifetime and a high Amount, causing many particles to spawn close to each other and fade out quickly.
We use a circle with blurred edges for the Textures -> Texture so our trail’s many particles blend together.
The Lifetime and Amount respectively dictate the trail’s length and quality. The more particles you add, the fewer artifacts you get.
In this demo, we set the Amount to 80 and the
Time -> Lifetime to 0.5.
We also turned off Drawing -> Local Coords so particles move independently of parent nodes. Otherwise, they’ll turn and move with the ship.
To make the trail taper off, we assign a to our Process Material -> Material and assign a to the Scale -> Scale Curve.
By making the curve go down, our particles scale down over their lifetime.
Also, we zero out the material’s Gravity -> Gravity as, by default, particles fall.
To give our spaceship two trails, we duplicate the node.
The node has an Emitting property that tells if it should or shouldn’t draw particles.
For this effect, we only want to draw particles when the spaceship is moving.
In the spaceship’s script, we toggle the left and right thrust trails
when the player presses or releases the move_up action.
onready var _trail_left := $TrailParticles2DLeft
onready var _trail_right := $TrailParticles2DRight
func _unhandled_input(event: InputEvent) -> void:
_trail_left.emitting = Input.is_action_pressed("move_up")
_trail_right.emitting = Input.is_action_pressed("move_up")Another use case for particles is to create dust puffs when the player characters land on the floor.
For this effect to work, we need a texture resembling clouds in the Texture property.
In this demo, we set the Lifetime to 0.7 as
dust dissipates fast.
As the dust puffs appear as soon as the feet touch the ground, we use
a high Explosiveness. In this demo, we set it to
0.9.
The particles should stay where the character’s feet touched the ground. So we also disable the Drawing -> Local Coords.
Then, for the particles’ behavior, we use a in the Process Material -> Material property.
In the , we disable gravity. So every axis of the Gravity property has a value of zero. This prevents them from the sense of weight.
We also set the Direction X-axis to 1.0 and the
Y and Z axes to 0.0. This makes the particles emit towards
the right. And we use a Spread of 24 to create a
cone of emission.
The Spread allows the particles to move in different
directions. But for it to work, we need some Initial Velocity.
So we set this property to 60.0.
To make the dust clouds puff, we play with the Scale -> Scale Curve. We use three points in the curve to draw this shape.
The graph means the particle starts with a small scale. Then it grows fast to the final scale. Then it shrinks until it disappears.
Note that we set the Scale -> Scale property to
0.6. This property controls the maximum scale of each
particle.
We use a gradient in the Color -> Color Ramp property to tint the texture and fade it at the end of its lifetime.
To use the effect in-game, we create a scene with two instances of the particles. Each of them spawns dust on both sides of the character.
In the left one, we have the unique to the
LeftPuffParticles2D. This is because we want to make the
particles move towards the left. To do so, you have to change the
Direction’s X-axis value to -1.0.
Although our dust is a one-time effect, we didn’t use the One Shot property here. It causes some issues with quick effects like the one we have.
Instead, we use an to control the emission. Our
animation also calls the queue_free() method on the root
node. With this approach, we remove it from the scene as soon as the
effect finishes.
With the animation set to autostart, it should play as soon as the effect appears on the scene.
All we have to do then is instantiate the DustPuffParticles scene when a character lands. For that, we used a Spawner. It’s a node responsible for adding nodes to the game world.
extends Position2D
export (PackedScene) var spawnling_scene
func spawn() -> Node2D:
var spawnling: Node2D = spawnling_scene.instance()
spawnling.set_as_toplevel(true)
spawnling.global_position = global_position
add_child(spawnling)
return spawnlingThen, we add a Spawner to the character’s scene and set its Spawnling Scene property to point to the DustPuffParticles2D scene file.
To trigger the effect we used the BasePlayerSideScroll
is_landing() method in the
DustPuffPlayerSideScroll.
## Returns `true` if the character is landing on the floor this frame.
func is_landing() -> bool:
# When the _snap_vector is zero, it means the character jumped
# If the character jumped then hit the floor it means it landed
return _snap_vector == Vector2.ZERO and is_on_floor() if is_landing():
_dust_puff.spawn()Splashing some water when the character enters a pool, river, or lake is a good addition to any game’s feel.
For this effect, we use a texture that resembles a droplet.
Since the droplets spread in all directions we use a high
Amount to maintain the effect’s shape. In the demo, we use a
value of 64.
For the Lifetime we set a value of 1.5. This
gives them enough time to animate properly and disappear with a natural
feel.
Here we use a value of 0.1 for the Preprocess
property. This property skips the particles drawing to a point ahead in
time. We use this to make the droplets appear in positions a bit away
from the emission point.
Almost all droplets must appear as soon as the character enters the
water. So we set the Explosiveness to 0.95.
Again, we toggle off the Local Coords property. This prevents the particles from moving with the node.
We want the particles to fall fast to a sense of weight to them. For
that, in the we use a value of
400.0 in the Gravity y-axis.
The particles should move upwards to give the idea that they come
from the water as the player entered it. For that, we set the
Direction Y axis to -1.0 and 0.0 in X
and Z axes.
The particles only move if they have an Initial Velocity. So
we set it 400.0. To add variation to the movement, we’ve
set the Velocity Random to 0.6.
This finishes the basic movement that each droplet should make.
Notice that it looks a bit weird as the texture doesn’t follow the movement. To fix that, we toggle on the Flags -> Align Y property. This setting uses the particle’s movement vector to update its rotation.
We can add randomness to each particle’s lifetime. This way they
disappear at different duration, which is more natural. For that, we set
the Time -> Lifetime Randomness to 0.5.
We also create some streams of water so that some particles move one
after the other in the same direction. The Trail -> Divisor
property takes the Amount and creates some particles trails. In
the demo, we use a value of 4. With that, it creates a
total of 16 trails.
With the Initial Velocity we set, the particles go too far
from the emission point. This breaks the effect’s shape. To fix that, we
want to decelerate them. This narrows the effect a bit. To decelerate
them we can use the Damping property. In the demo, we’ve set it
to 60.0 and the Damping Random to
0.3.
Finally, to make the droplets look like they are dissipating, we animate their scale.
In the Scale category, we set the Scale to
0.5 to decrease the particles’ maximum size. Then, to add
variation to their scale we’ve set the Scale Random to
0.25. Then we use the following curve to animate their size
along their lifetime:
As you can see, they start with half their final scale, then shrink and disappear. This gives a sense of depth. They grow a bit as if they were moving towards the camera.
To tint the texture, we create a in the Color -> Color Ramp property. The gradient goes from a semi-transparent blue to a completely transparent one at the end. With that, the droplets fade as they get closer to their lifetime duration.
To emit this effect we use a similar approach to the Dust
Puff. We use an with an autoplay animation to
control the Emit property. It also calls the
queue_free() method on the root node.
And like the Dust Puff, to use this effect in practice we rely on a Spawner in the player’s character scene.
To trigger the spawn of the effect we use an to detect when the character touches a water area.
func _ready() -> void:
_water_detecting_area.connect("area_entered", self, "_on_WaterDetectingArea2D_area_entered")Then we use a threshold to know if the speed the character fell on the water was enough to create a splash.
func _on_WaterDetectingArea2D_area_entered(area: Area2D) -> void:
if _velocity.y < splash_fall_threshold:
return
_water_splash_spawner.spawn()It’s interesting to provide visual feedback when something hurts the player’s character. Spilling blood at the point of injury is a good way to do that.
For the blood spills, we use an approach like the water splash shown before. The properties’ values change, but the concept is the same. For reference, these are the settings for the BloodParticles2D node:
161.0true0.97falseAs for the :
true12Vector3(0.0, 600.0, 0.0)300.00.550.4Unlike the water splash, the BloodParticles2D goes as a direct child to the player character.
The way we trigger its emission is through a method that we call on the player when something hurts them.
func get_hurt(motion: Physics2DTestMotionResult) -> void:
_blood_particle.rotation = motion.collision_normal.angle()
_blood_particle.global_position = motion.collision_point
_blood_particle.emitting = true
_anim_player.play("hurt")Note that the method asks for a motion parameter. This is a
Physics2DTestMotionResult. It has all the collision data we
need to rotate and position the effect. We do that to make the effect
fit the injury position and the movement direction.
So, to provide this data, we use objects that can hurt other objects.
Upon collision, we create a Physics2DTestMotionResult
testing the movement towards the collision body. Then we check if they
collided with something hurtable. For that, we check if the object has
the get_hurt() method. If this is the case, we call the
method on the object.
func _on_body_entered(body: Node) -> void:
var motion_test_result := Physics2DTestMotionResult.new()
test_motion(body.global_position, true, 0.08, motion_test_result)
_pivot.rotation = motion_test_result.collider_velocity.angle()
if body.has_method("get_hurt"):
body.get_hurt(motion_test_result)With that, no matter where the character got hurt, blood spills there!
Using the right textures, we can simulate many weather and environmental effects such as rain, snow, or leaves falling on an autumn day. We can even make sakura petals fall: an interesting effect for Japanese visual novels.
| Spring | Summer | Autumn | Winter |
|---|---|---|---|
Each of these effects uses different Texture, Amount, Lifetime, and settings. So we are going to focus on what they have in common: Emission Shape.
In the we can play with different Emission Shapes. Until now we created our particles from a single point. But we can do it differently.
There are five types of Emission Shapes, here we focus on the three main ones: Point, Sphere, and Box. We talk about Points and Directed Points in the Emission Masks section.
| Point | Sphere | Square |
|---|---|---|
In our demo, we use the Square shape with X equal to the
screen’s width, in this case, 1920.0. With that, the
particles can spawn anywhere across the screen. So we move the to the top portion of the scene. Then,
we increase the ParticlesMaterial -> Gravity.
This makes it look like they are falling from the sky. This approach is good for side view games.
There’s also a bonus environment effect we use in some of our space-themed scenes: the starfield. It uses the same concept, but with X and Y axes corresponding to the screen width and height. In this one, we zero out the ParticlesMaterial -> Gravity, as these stars aren’t meant to fall.
Many games have attacks that players can charge before firing. For that, they gather energy from the environment. For example, in Mega Man X series, we can charge the X Buster for a powerful shot.
Let’s see how to make this effect using and an .
The Charge effect consists of two core aspects:
For the effect’s Texture, we use an image of two
overlapping circles. A solid one and a blurry one, which simulates
glowing.
The setup consists of a total
Amount of 16 particles. We also set the Local
Coords to false and that’s it. The real trick comes in the properties.
To create the energy area around the character we change the
Emission Shape -> Shape to a Sphere. It should have
60.0 pixels of radio.
For that movement where the particles go towards the center, we did the following:
0.0.-80.0.0.3.Radial acceleration moves the particles based on the position. So a negative value moves them towards it, while a positive moves them away from the emission’s center.
| 100 Radial Accel | -100 Radial Accel |
|---|---|
Finally, we created this curve for the Scale -> Scale
Curve. It makes it so that the particles look like appearing from
thin air as the caster charges the energy. We also decrease the
Scale -> Scale to 0.5. And for a variation on
their size, we set the Scale -> Scale Random to
0.3.
Now, to create the feeling of anticipation, we use an animation. Here we play with the Particles2D -> Speed Scale. The Speed Scale multiplies the particles’ processing speed.
We animate it from 0.5 to 2.0 within a
second. With that, we can play with slowed and accelerated versions of
the effect.
If your computer supports GLES3, you can also play with the Self
Modulate property. Here we animate it from default white to
1.5 white. With that, the particles glow dynamically in a
scene with a World Environment that has Glow turned
on.
We can play this animation to use the effect in the final scene.
The demo has another animation that changes the properties in other nodes. As an example, it triggers the laser beam and for the charge-up UI. We also play the inside this animation as well.
By relying on the like that, the actual code becomes quite small:
func _unhandled_input(event: InputEvent) -> void:
if event.is_action_pressed("shoot"):
_anim_player.play("charge")
# Resets the character's pose
if event.is_action_released("shoot") and can_cancel:
_anim_player.play("RESET")We can also use to simulate effects that would be too complex to draw by hand. One of these things is fire and smoke.
Using textures and a gradient, we can simulate the look of a rocket thrust using particles.
Using the ParticlesMaterial -> Color Ramp property, we can tint the texture’s color over time. That’s the most important feature we use in this example.
Here are the base properties we use for this effect:
64. As always, we picked this around
the end of the process, looking at our trail effect.1 second.false, so the puffs don’t turn
with the parent ship.Next is the .
There, we set the Direction -> Direction to point
downwards. So the values for each axis are X of 0.0, Y of
1.0, and Z of 0.0. We do that to match the
spaceship’s exhaust fan position in the .
As in many other effects, we zero out the Gravity to prevent the particles from falling.
To create cone-like emission, we set the Direction ->
Spread to 15.0 degrees.
We need a value above zero in the Initial Velocity ->
Velocity for the material to take the emission direction into
account. So we set this property to the arbitrary value of
1.0.
To make the whole trail look attractive, we animate the particles’ angle.
To do so, we add some Angular Velocity -> Velocity and randomize the value.
To randomize the start angle of each particle, we change the Angle -> Angle and Angle -> Angle Random properties. This adds visual variation to our particles and makes the trail look appealing.
To make it look like the smoke dissipates, we play with the Scale -> Scale and Scale -> Scale Curve. Smoke spreads in the air, so we increase the scale along the particle’s lifetime to get that feel.
To make the trail look like fire, we assign a color gradient to the Color -> Color Ramp property.
This property tints the particle along its lifetime.
So to make it look like fire, we start with white, then yellow, orange, red, dark gray, then the same dark gray but transparent.
This last transparent color makes the particles fade out instead of disappearing instantly.
Combining those settings leads to an appealing engine trail.
Note that to recolor the particles, you need a white texture like our transparent puff.
The color options tint the texture by multiplying the colors.
If your texture uses any tone other than pure white, the color ramp will darken the texture.
This behavior is consistent with the Modulate property of 2D nodes in Godot.
Explosions are one of the main visual effects game developers can have on their sleeves. There are two main approaches to creating explosions:
| Flipbook | Result |
|---|---|
There’s also a middle ground. You can play with some pre-rendered images in a procedural explosion. You get the best of both worlds and diminish the downsides.
In our demo, we used stylized graphics. So we can go straight with the engine simulation approach using simplified textures.
This effect shows how to layer nodes to create complex and rich results. Four layers compose the explosion:
| Smoke | Fire trails | Fire burst | Sparkles |
|---|---|---|---|
The smoke layer uses a long Lifetime to dissipate the explosion smoke slowly.
The fire trail is a composition of five . We use the ParticlesMaterial -> Trail properties to simulate flying debris.
As for the fire burst, we quickly grow then shrink many particles. The trick is the short Lifetime, high Amount, and the Scale Curve.
Then, for the sparks, we play with Scale Curve as well, but also with Radial Accel and Tangential Accel.
This effect is part of our 2D VFX Secrets course. There we go in-depth in it. So if you want to know more in detail, you can follow up there.
To coordinate all these layers together and trigger them in sequence, we use an animation.
This animation automatically plays as soon as the effect enters the scene.
The animation toggles the emission of each . Note that at the end, we call the
queue_free() method on the Explosion node.
This explosion plays once then disappears. We instantiate a new copy every time we need to play the explosion animation.
To enrich the scenarios of our games we can add all sorts of visual effects. For instance, in space games, it’s cool to have a black hole here and there. We can even turn the effect into a mechanic that pulls objects towards it.
We can make the visual part of these effects with some properties. The main ones are:
We’ve already seen how Radial Acceleration works. So let’s see Orbit Velocity in action now:
Orbit Velocity 0.2 |
Orbit Velocity 1.0 |
|---|---|
You can already see how these properties can work together to create a whirl, right? One pulls particles towards the center, the other spins particles around the center.
In the Blackhole demo, we’ve set up the Blackhole as below:
There’s an animation that autoplays. It makes the blackhole spin.
Then, in the script, we use the to animate the Scale from
Vector2(1.0, 1.0) to Vector2(1.5, 1.5) and
back. We use TRANS_ELASTIC and TRANS_BACK for
better interpolation.
The basic setup for the is the following:
2.0falseThen for the :
80.0. Bigger than the
Blackhole sprite to make stars appear outside it.0.0 on all three axes.50.0.50.0. To spin
the stars as they move.0.3.
To add variety to their spin velocity.We use this curve to speed up the spin as the stars move.
As for the Orbit Velocity we use a trick. We want to add depth to the movement. So we use the following settings:
With this curve, the stars orbit slower as they get closer to the blackhole’s center. This approach gives the sensation of distance.
We also use these values in the Scale properties:
We designed the curve above to give the idea that the stars appear from deep space. Then they move closer to the camera. Finally, the blackhole sucks them in, making them shrink to distance.
And this is a brief glance at the final look.
Note that we use a special with Glow. So we’ve bumped up the Particles2D -> Modulate to the following values:
Imagine that your characters entered in “awakening” mode. You may want some energy to flow around them, right?
For this type of effect, the default shapes in the ParticlesMaterial -> Emission Shape aren’t enough. Instead, we can use an Emission Mask.
There are two kinds of Emission Masks. In the below example we have the image we’ve used for the mask. Then the three types of Emission Masks.
| Mask | Solid Pixels | Border Pixels | Directed Border Pixels |
|---|---|---|---|
Note that the particles don’t appear at the emission center. This is due to how Godot maps the image points.
For more details on how these three modes work, you can read the Godot official documentation on Emission Masks.
In our demo, we use the Border Pixels option to map the particles around the spaceship.
The settings for the AuraParticles2D follow:
1280.1falseAs for the , we work in the Scale category. These changes make the particles look like sparks.
The final touch is using this effect in a with Glow active. Then, we bumped up the AuraParticles2D Modulate. Remember, to trigger the glow the color must be above the WorldEnvironment -> Glow -> HDR Threshold.
The design behind this effect is that once players enter “turbo mode,” they emit this aura as if the spaceship is surrounded by pure energy. So, these particles aren’t always emitting. Instead, they only emit when the player presses the turbo key.
You can, of course, add some extra limitations. As an example, you can add some special fuel or energy that depletes in turbo mode. But in our case is just a matter of pressing a button:
# Turbo effect
if Input.is_action_just_pressed("turbo"):
speed = turbo_speed
_aura.emitting = true
elif Input.is_action_just_released("turbo"):
speed = max_speed
_aura.emitting = falseIf your game uses the GLES2 renderer, you won’t have access to the
node. Instead, you’ll need to use
CPUParticles2D.
You can follow the guides using the CPUParticles2D node
instead of . The properties will be in the node
itself instead of part of a material resource.
With GLES2, you can’t use graphics acceleration to move the
particles. The CPUParticles2D uses the processor (CPU)
instead of the graphics card to move particles. This is a constraint
imposed by the older GLES2 technology.
When using CPUParticles2D, you’ll need to be careful
with the number of particles you spawn, as they’ll compete with the rest
of your code.
Yes. You can turn it into a and tweak it as you please using the shader editor.
For that, right-click the Material -> ParticlesMaterial resource and select Convert to ShaderMaterial.
We use this approach when we have a texture with a sequence of images that animate along a particle lifetime.
To do that, we need to create a in the Material -> Material property. Then we toggle on the Particles Animation property. After that, we set up the flipbook frames according to the texture.
Then in the , there’s a category of properties to control how the animation should play along the particle’s lifetime.
We recommend looking at the official docs for and shaders:
Then, to go further, we have the 2D VFX Secrets course.
We use particles extensively there and go deep into how each effect works.